import { CCInteger, Node, Component, Mat4, Vec3, _decorator } from 'cc';
import { Collision } from './core/Collision';
import { Debug } from './core/Debug';
import { ObjNode } from './core/ObjNode';
import { Octree, GetMaxLevel } from './core/Octree';
import { Object3D } from './Object3D';



 class World {

    debug: boolean = false;
    maxLevels: number = 4;
    maxObjects: number = 8;
    center: Vec3 = new Vec3();

    halfSize: Vec3 = new Vec3(50, 50, 50);


    octree: Octree;
    flag: number = 0;
    result: Array<ObjNode> = [];
    objects: Array<Object3D> = [];
    collision: Collision = new Collision();


    preBuild(parent:Node) {

        let c = this.center;
        let h = this.halfSize;
        Vec3.max(h, h, Vec3.ONE);
        let bound = [c.x, c.y, c.z, h.x, h.y, h.z];
        this.octree = new Octree(bound, (this.maxObjects << 8) | this.maxLevels);
        this.initObjs(parent);


    }
    initObjs(parent:Node) {
        let objects = parent.getComponentsInChildren(Object3D);
        for (let i = 0; i < objects.length; i++) {
            this.insert(objects[i]);
        }
    }

    insert(object: Object3D) {

        let bounds = object.updateBounds(true);
        if (bounds.length) {
            this.objects.push(object);
            let objNodes = object.objNodes;
            if (objNodes && objNodes.length > 0) { // reInsert
                for (let j = 0; j < objNodes.length; j++) {
                    this.octree.insert(objNodes[j]);
                }
                return;
            }

            for (let j = bounds.length - 1; j >= 0; j--) {

                let objNode = new ObjNode(object, object.id, j, bounds[j]);
                GetMaxLevel(objNode, this.octree, 1.0); //prebuild max level
                this.octree.insert(objNode);
                objNodes.push(objNode);

            }
        }

    }

    remove(object: Object3D, isClear: boolean = true) {
        let objects = this.objects;
        let idx = objects.indexOf(object);
        if (idx != -1) {
            //remove from octree
            let objNodes = object.objNodes;
            for (let i = 0; i < objNodes.length; i++) {
                this.octree.remove(objNodes[i]);
            }

            let end = objects.length - 1;
            objects[idx] = objects[end];
            objects.length = end;
        }

        isClear && object.clear();
    }

    aabbQuery(worldBound: Array<number>, isTriangle: boolean = false, mask: number = 0xFFFFFFFF) {

        this.result.length = 0;
        this.octree.retrieve(worldBound, ++this.flag, this.result, mask);//, this.debug);

        if (isTriangle) {
            let objs = this.result;
            let length = objs.length;
            for (let i = 0; i < length; i++) {
                let objB = objs[i].obj3D;
                if (objB.useTriangle) {
                    // Octree.count = 0;
                    let result = objB.boundTriangles(worldBound);
                    this.result.splice(i, 1);
                    for (let j = result.length - 1; j >= 0; j--) {
                        this.result.push(result[j]);
                    }
                    result.length = 0;
                    length--;
                    i--;
                }
            }
        }


        return this.result;
    }


    objectQuery(objA: Object3D, isTriangle: boolean = false) {

        this.result.length = 0;
        let worldBound = objA.getWorldBound();
        this.octree.retrieve(worldBound, ++this.flag, this.result, objA.mask);
        if (isTriangle) {

            let result = this.result;
            let length = result.length;
            for (let i = length - 1; i >= 0; i--) {
                let objB = result[i].obj3D;
                if (objB.useTriangle) {

                    //add triangles from mesh
                    let tris = objB.boundObject(objA);
                    for (let j = 0; j < tris.length; j++) {
                        result[length++] = tris[j];
                    }
                    // tris.length = 0;
                    result[i] = result[--length]; //remove the mesh
                }
            }

            result.length = length;

        }

        return this.result;
    }


    forceClear() {
        for (let i = 0; i < this.objects.length; i++) {
            this.objects[i].clear(); //release mesh data
        }

        this.clear();
    }

    clear() {

        this.objects.length = 0;

        if (this.octree) {
            this.octree.clear();
            this.octree = null;
        }


    }

}
const  world = new World;
export default  world;
